package org.wheres2.core;

import java.util.Collection;
import java.util.Iterator;
import java.util.Map;

import org.jivesoftware.smack.AccountManager;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.Roster.SubscriptionMode;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.OrFilter;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.util.StringUtils;
import org.wheres2.core.contactlist.InvalidContactNameException;
import org.wheres2.core.thread.ConnectionThread;
import org.wheres2.utils.MessageMod;

import android.location.Location;
import android.location.LocationManager;
/**
 * 
 * La classe implementa il CommunicationManager.
 * Si occupa dell'interfacciamento col server.
 * 
 * @author 
 *
 */
public class OpenFireCommunicator implements CommunicationManager {

	private XMPPConnection connection;
	private ConnectionConfiguration config;
	private ConnectionThread connectionThread;
	private Roster roster;
	private String server = "wheres.homepc.it";
	private int port = 5222;
	private StateManager client;
	
	public OpenFireCommunicator(StateManager stateManager) {
		this.client = stateManager;
		this.config = new ConnectionConfiguration(server, port);
		this.config.setSASLAuthenticationEnabled(false);
		this.connection = new XMPPConnection(this.config);
		connectionThread = new ConnectionThread(this.connection);
		connectionThread.start();
	}
	
	/**
	 * 
	 * @see org.wheres2.core.CommunicationOperations#sendMessage(String, String)
	 * 
	 */
	@Override
	public void sendMessage(String message, String to) {
		Message msg = new Message();
		msg.setTo(to);
		msg.setBody(message);
		msg.setType(Message.Type.chat);
		connection.sendPacket(msg);

	}
	
	/**
	 * 
	 * @see org.wheres2.core.CommunicationOperations#addContactToServer(String, String)
	 * 
	 */
	@Override
	public void addContactToServer(String contact, String name)
			throws InvalidContactNameException, XMPPException {
		if(this.roster.contains(contact)){
			RosterEntry rosterEntry = this.roster.getEntry(contact);
			rosterEntry.setName(name);		
		}
		this.roster.createEntry(contact, name, null);
		
	}
	
	/**
	 * 
	 * @see org.wheres2.core.CommunicationOperations#removeContactFromServer(String)
	 * 
	 */
	@Override
	public void removeContactFromServer(String contact)
			throws InvalidContactNameException, XMPPException {
			RosterEntry entry = this.roster.getEntry(contact);
			this.roster.removeEntry(entry);
		
	}

	/**
	 * 
	 *  @see org.wheres2.core.CommunicationOperations#startConnection(String, String)
	 *  
	 */
	@Override
	public void startConnection(String userName, String userPassword)
			throws XMPPException {
		
		if(this.connection.isConnected()){
			connection.login(userName, userPassword);
		}
		
		this.roster = this.connection.getRoster();
		Roster.setDefaultSubscriptionMode(SubscriptionMode.accept_all);
		this.roster.setSubscriptionMode(SubscriptionMode.accept_all);
		this.roster.addRosterListener(this);
		
		this.connection.addPacketListener(this, new OrFilter(
									new PacketTypeFilter(Message.class),
									new PacketTypeFilter(Presence.class)
									));
		this.connection.addConnectionListener(this);
		
		this.initRoster();
		
		this.client.setConnectionState(true);
		

	}

	/**
	 * 
	 * @see org.wheres2.core.CommunicationOperations#createAccount(String, String, Map)
	 * 
	 */
	@Override
	public void createAccount(String userName, String userPassword,
			Map<String, String> userProp) throws XMPPException {
		
		if(this.connection.isConnected()){
			AccountManager accountManager = new AccountManager(this.connection);
			accountManager.createAccount(userName, userPassword, userProp);
		}

	}

	/**
	 * 
	 * @see org.wheres2.core.CommunicationOperations#disconnect()
	 * 
	 */
	@Override
	public void disconnect() {
		if(this.connection.isConnected()){
			this.connection.disconnect();
			try {
				connectionThread.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			this.client.setConnectionState(false);
		}
	}

	/**
	 * 
	 * @see org.wheres2.core.CommunicationOperations#updateRoster()
	 * 
	 */
	@Override
	public void updateRoster() {
		this.roster.reload();

	}

	/**
	 * 
	 * @see org.jivesoftware.smack.PacketListener#processPacket(Packet)
	 * 
	 */
	@Override
	public void processPacket(Packet arg0) {
		  if(arg0 instanceof Message){
			  String from = arg0.getFrom();
			  String message = ((Message) arg0).getBody();
			  if (((Message) arg0).getType().equals(Message.Type.chat)){
				  int type = MessageMod.getMessageType(message);
				  if(type == MessageMod.REQUEST){
					  this.client.receivedMessageNotification(from, null, MessageMod.REQUEST);
				   	
				  }else if(type == MessageMod.POSITION){
					  Location location = new Location(LocationManager.GPS_PROVIDER);
					  String posValues[] = MessageMod.extractValues(message);
					  location.setLatitude(Double.parseDouble(posValues[0]));
					  location.setLongitude(Double.parseDouble(posValues[1]));
					  location.setAccuracy(Float.parseFloat(posValues[2]));
					  location.setTime(Long.parseLong(posValues[3]));
					  this.client.receivedMessageNotification(from, location, MessageMod.POSITION);

				  }
			  }
		  }

	}

	/**
	 * 
	 * @see org.jivesoftware.smack.RosterListener#entriesAdded(Collection)
	 * 
	 */
	@Override
	public void entriesAdded(Collection<String> arg0) {
		
		String tempContact;
		RosterEntry tempEntry;
		String name;
		Iterator<String> it= arg0.iterator();
		while(it.hasNext()){
			tempContact=(String)it.next();
			tempEntry = this.roster.getEntry(tempContact);			
			name = tempEntry.getName();
			if(name==null){
				int end=StringUtils.parseBareAddress(tempContact).indexOf("@");
				tempEntry.setName(StringUtils.parseBareAddress(tempContact).substring(0,end ));
				name=tempEntry.getName();
			}
				try {
					this.addContactToServer(tempContact, name);
				} catch (InvalidContactNameException e) {
					e.printStackTrace();
				} catch (XMPPException e) {
					e.printStackTrace();
				}
			
		}
			
		
	}
	
	/**
	 * 
	 * @see org.jivesoftware.smack.RosterListener#entriesDeleted(Collection)
	 * 
	 */
	@Override
	public void entriesDeleted(Collection<String> arg0) {

		String tempContact;
		Iterator<String> it= arg0.iterator();
		while(it.hasNext()){
			tempContact = it.next();
			this.client.removeContact(tempContact);
		}
			
	}

	/**
	 * 
	 * @see org.jivesoftware.smack.RosterListener#entriesUpdated(Collection)
	 * 
	 */
	@Override
	public void entriesUpdated(Collection<String> arg0) {
		
	}

	/**
	 * 
	 * @see org.jivesoftware.smack.RosterListener#presenceChanged(Presence)
	 * 
	 */
	@Override
	public void presenceChanged(Presence arg0) {
		
		String user = StringUtils.parseBareAddress(arg0.getFrom());
		String name = this.roster.getEntry(user).getName();
		this.client.setContactPresence(user, name, arg0.isAvailable());

	}
	
	/**
	 * 
	 * Inizializza la copia locale della roster
	 *  
	 */
	private void initRoster(){
		Iterator<RosterEntry> it =  this.roster.getEntries().iterator();
		
		RosterEntry rosterEntry;
		boolean online;
		
		while(it.hasNext()){
			rosterEntry = (RosterEntry)it.next();
			online = this.roster.getPresence(rosterEntry.getUser()).isAvailable();
			this.client.addContact(rosterEntry.getUser(), rosterEntry.getName(), online);
			this.roster.getPresence(rosterEntry.getUser());//richiede un messaggio di presenza per il contatto
			}
	
		
	}

	/**
	 * 
	 * @see org.wheres2.core.CommunicationOperations#setContactNameOnServer(String, String)
	 * 
	 */
	@Override
	public void setContactNameOnServer(String contact, String name) {
		if(this.roster.contains(contact)){
			RosterEntry rosterEntry = this.roster.getEntry(contact);
			rosterEntry.setName(name);		
		}
		
	}

	/**
	 * 
	 * @see org.jivesoftware.smack.ConnectionListener#connectionClosed()
	 * 
	 */
	@Override
	public void connectionClosed() {
		this.client.setConnectionState(false);
		
	}

	/**
	 * 
	 * @see org.jivesoftware.smack.ConnectionListener#connectionClosedOnError(Exception)
	 * 
	 */
	@Override
	public void connectionClosedOnError(Exception arg0) {
		this.client.setConnectionState(false);		
	}

	/** 
	 * 
	 * @see org.jivesoftware.smack.ConnectionListener#reconnectingIn(int)
	 * 
	 */
	@Override
	public void reconnectingIn(int arg0) {
		
	}

	/**
	 * 
	 * @see org.jivesoftware.smack.ConnectionListener#reconnectionFailed(Exception)
	 * 
	 */
	@Override
	public void reconnectionFailed(Exception arg0) {
		
	}
	
	/**
	 * 
	 * @see org.jivesoftware.smack.ConnectionListener#reconnectionSuccessful()
	 * 
	 */
	@Override
	public void reconnectionSuccessful() {
	}

	/**
	 * 
	 * @see org.wheres2.core.CommunicationOperations#isLoggedOnServer()
	 * 
	 */
	@Override
	public boolean isLoggedOnServer() {
		return this.connection.isAuthenticated();
	}


}

